package grape.workflow.rest.task.mvc;

import cn.hutool.core.util.StrUtil;
import grape.common.exception.ExceptionTools;
import grape.common.exception.runtime.InvalidParamsException;
import grape.common.exception.runtime.RBaseException;
import grape.common.exception.runtime.RDataNotExistException;
import grape.common.tools.ToolService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.FlowNode;
import org.flowable.bpmn.model.SequenceFlow;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskInfo;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 任务逆向跳转帮助类
 */
@Slf4j
@Component
public class TaskMoveReverseHelper implements ToolService {

    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private HistoryService historyService;
    @Autowired
    protected RepositoryService repositoryService;
    /**
     * 任务节点跳转
     * @param currentActivityId 从哪个节点,当前节点
     * @param toActivityId 到哪个节点，可支持的跳转目标节点
     * @param processInstanceId 流程实例id
     */
    public void taskMoveByActivityId(String currentActivityId, String toActivityId,String processInstanceId) {
        if (StrUtil.equals(currentActivityId,toActivityId)) {
            throw ExceptionTools.invalidParamsRE("目标任务和当前任务相同，不能跳转");
        }

        if(!isSupportTaskMove(currentActivityId,toActivityId,processInstanceId)){
            throw ExceptionTools.invalidParamsRE("暂时不支持目标任务的跳转");
        }
        runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId).moveActivityIdTo(currentActivityId,toActivityId).changeState();
    }

    /**
     * 当前支持的可移动的历史节点
     * @param currentActivityId
     * @param processInstanceId
     * @return UserTask 类型元素
     */
    public List<FlowElement> supportedHistoryFlowElements(String currentActivityId,String processInstanceId){

        return getFlowElements(currentActivityId,processInstanceId,false,false);
    }

    private BpmnModel getBpmnModelByProcessInstanceId(String processInstanceId) {
        String processDefinitionId = null;
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if (processInstance == null) {
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            processDefinitionId = historicProcessInstance.getProcessDefinitionId();
        }else {
            processDefinitionId = processInstance.getProcessDefinitionId();
        }
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        return bpmnModel;
    }

    /**
     * 获取指定节点前面或后面的任务节点
     * @param activityId
     * @param processInstanceId
     * @param isOutGoing
     * @param isOutGoing 是否相邻的
     * @return 目前只支持相邻的
     */
    public List<FlowElement> getFlowElements(String activityId, String processInstanceId,boolean isOutGoing,boolean adjacent) {
        List<FlowElement> result = new ArrayList<>();

        if (isStrEmpty(processInstanceId)) {
            return result;
        }
        BpmnModel bpmnModel = getBpmnModelByProcessInstanceId(processInstanceId);
        FlowElement flowElement = bpmnModel.getFlowElement(activityId);
        FlowNode currentFlowNode = flowElement instanceof FlowNode ? ((FlowNode) flowElement) : null;
        if (currentFlowNode == null) {
            return result;
        }
        if(isOutGoing){
            for (SequenceFlow incomingFlow : currentFlowNode.getOutgoingFlows()) {
                if (incomingFlow.getTargetFlowElement() instanceof org.flowable.bpmn.model.UserTask) {
                    result.add(incomingFlow.getTargetFlowElement());
                }
            }
        }else {
            for (SequenceFlow incomingFlow : currentFlowNode.getIncomingFlows()) {
                if (incomingFlow.getSourceFlowElement() instanceof org.flowable.bpmn.model.UserTask) {
                    result.add(incomingFlow.getSourceFlowElement());
                }
            }
        }
        return result;
    }

    /**
     *
     * @param taskId
     * @param isOutGoing
     * @return
     */
    public List<FlowElement> getFlowElements(String taskId,boolean isOutGoing,boolean adjacent){
        TaskInfo taskInfo = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (taskInfo == null) {
            taskInfo = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
        }
        if(taskInfo == null){
            return null;
        }
        return getFlowElements(taskInfo.getTaskDefinitionKey(), taskInfo.getProcessInstanceId(), isOutGoing,adjacent);
    }

    /**
     * 是否支持节点移动
     * @param currentActivityId
     * @param toActivityId
     * @param processInstanceId
     * @return
     */
    public boolean isSupportTaskMove(String currentActivityId, String toActivityId,String processInstanceId){
        List<FlowElement> flowElements = null;
        flowElements = supportedHistoryFlowElements(currentActivityId, processInstanceId);

        FlowElement flowElement = flowElements.stream().filter(item -> StrUtil.equals(toActivityId, item.getId())).findFirst().orElse(null);
        return flowElement != null;
    }

    /**
     *
     * @param nowTaskId 任务id
     * @param historyTaskId 历史任务id
     * @return
     */
    public boolean isSupportTaskMove(String nowTaskId,String historyTaskId) {
        Task nowTask = taskService.createTaskQuery().taskId(nowTaskId).singleResult();
        HistoricTaskInstance historyTask = historyService.createHistoricTaskInstanceQuery().taskId(historyTaskId).singleResult();
        if (nowTask == null || historyTask == null || !nowTask.getProcessInstanceId().equals(historyTask.getProcessInstanceId())) {
            return false;
        }
        return isSupportTaskMove(nowTask.getTaskDefinitionKey(), historyTask.getTaskDefinitionKey(), nowTask.getProcessInstanceId());
    }
        /**
         * 任务节点跳转
         * @param nowTaskId 当前待办的任务id
         * @param historyTaskId 已办的任务id
         */
    public void taskMoveByTaskId(String nowTaskId,String historyTaskId){
        if (StrUtil.equals(nowTaskId,historyTaskId)) {
            throw ExceptionTools.invalidParamsRE("目标任务和当前任务相同，不能跳转");
        }

        Task nowTask = taskService.createTaskQuery().taskId(nowTaskId).singleResult();
        if (nowTask == null) {
            throw ExceptionTools.dataNotExistRE("当前任务不存在");
        }
        if (nowTask.getProcessInstanceId() == null) {
            throw ExceptionTools.invalidParamsRE("当前任务不支持该操作，流程实例不存在");
        }
        HistoricTaskInstance historyTask = historyService.createHistoricTaskInstanceQuery().taskId(historyTaskId).singleResult();
        if (historyTask == null) {
            throw ExceptionTools.dataNotExistRE("目标任务不存在");
        }
        if (historyTask.getProcessInstanceId() == null) {
            throw ExceptionTools.invalidParamsRE("目标任务不支持该操作，流程实例不存在");
        }
        // 确保现在流程实例相同
        if (!nowTask.getProcessInstanceId().equals(historyTask.getProcessInstanceId())) {
            throw ExceptionTools.invalidParamsRE("目标任务和当前任务不在同一个流程实例中");
        }

        taskMoveByActivityId(nowTask.getTaskDefinitionKey(),historyTask.getTaskDefinitionKey(),nowTask.getProcessInstanceId());
    }

    /**
     * 判断一个历史任务是否支持撤回
     * @param historyTaskId
     * @param throwException
     * @return
     */
    public boolean isCanRecall(String historyTaskId,boolean throwException){
        try {
            NextRunningTasks nextRunningTasks = nextRunningTasks(historyTaskId);

        } catch (RBaseException e) {
            if(throwException){
                throw e;
            }else {
                return false;
            }
        }
        return true;
    }

    /**
     * 根据一个历史任务id获取下一步运行中的任务，主要用于撤回
     * 如果不存在将会抛出异常
     * @param historyTaskId
     * @return 下一步的运行中任务
     * @throws InvalidParamsException,RDataNotExistException
     */
    public NextRunningTasks nextRunningTasks(String historyTaskId){
        HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().taskId(historyTaskId).singleResult();
        if (historicTaskInstance == null) {
            throw ExceptionTools.dataNotExistRE("任务不还在");
        }
        if (isStrEmpty(historicTaskInstance.getProcessInstanceId())) {
            throw ExceptionTools.invalidParamsRE("当前任务不支持撤回");
        }

        List<FlowElement> flowElements = getFlowElements(historyTaskId, true,true);
        if (isEmpty(flowElements)) {
            throw ExceptionTools.invalidParamsRE("当前任务不支持撤回");
        }
        List<Task> list = taskService.createTaskQuery()
                .taskDefinitionKeys(flowElements.stream().map(FlowElement::getId).collect(Collectors.toSet()))
                .processInstanceId(historicTaskInstance.getProcessInstanceId())
                .list();
        if (isEmpty(list)) {
            throw ExceptionTools.invalidParamsRE("当前任务不支持撤回或下一个节点任务已处理");
        }
        NextRunningTasks nextRunningTasks = new NextRunningTasks();
        nextRunningTasks.setHistoricTaskInstance(historicTaskInstance);
        nextRunningTasks.setFlowElements(flowElements);
        nextRunningTasks.setNextRunningTasks(list);
        return nextRunningTasks;
    }
    @Data
    public class NextRunningTasks{
        protected HistoricTaskInstance historicTaskInstance;
        protected List<FlowElement> flowElements;
        List<Task> nextRunningTasks;
    }

    /**
     * 可打回的已办历史节点任务实例
     * @param toDoTaskId
     * @return
     */
    public List<HistoricTaskInstance> historyTasksThatCanReject(String toDoTaskId){
        Task task = taskService.createTaskQuery().taskId(toDoTaskId).singleResult();
        List<FlowElement> flowElements = supportedHistoryFlowElements(task.getTaskDefinitionKey(), task.getProcessInstanceId());
        if (isEmpty(flowElements)) {
            throw ExceptionTools.dataNotExistRE("暂无可驳回的任务");
        }
        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().finished().processUnfinished()
                .taskDefinitionKeys(flowElements.stream().map(FlowElement::getId).collect(Collectors.toSet()))
                .processInstanceId(task.getProcessInstanceId()).orderByTaskCreateTime().desc()
                .list();

        if (isEmpty(list)) {
            throw ExceptionTools.dataNotExistRE("暂无可驳回的任务");
        }
        // 根据taskDefinitionKey 去重，去重不用担心任务id不一致，因为只关心activityId
        return list.stream().filter(distinctByKey(item -> item.getTaskDefinitionKey())).collect(Collectors.toList());
    }
}
